import c4d
import os
import math
import base64

from c4d import gui, plugins, bitmaps

PLUGIN_ID = 1037225
C4D_DISPLAY_TEXTURE = 1000
UNITY_CONTENT = 1001
ANALYZE_BTN = 1002
OUTPUT_BTN = 1003

def FirstOrDefault(collection, func):
    result = filter(func, collection)
    
    if len(result) == 0: return None
    
    return result[0]

def CacheTexturesPath(node):

    result = list()
    
    for id, bc in node.GetUserDataContainer():
        
        if type(node[id]) == str and bc[c4d.DESC_PARENTGROUP] == None:

            name = bc[c4d.DESC_NAME]
            value = node[id]
            result.append((name, value))
        
    return result

def ClearUserData(node):
    for id, bc in node.GetUserDataContainer():
        del(node[id]) 
        node.RemoveUserData(id)
        
    c4d.EventAdd()

def AddUserData(obj, type, unit, name, group, value):
    if obj is None: return

    bc = c4d.GetCustomDataTypeDefault(type)
    bc[c4d.DESC_NAME] = name
    bc[c4d.DESC_UNIT] = unit
    bc[c4d.DESC_ANIMATE] = c4d.DESC_ANIMATE_OFF
    bc[c4d.DESC_PARENTGROUP] = group

    element = obj.AddUserData(bc)
    if type == c4d.DTYPE_COLOR and "(rgb)" in name:
        strCollection = value.split("_")
        r = float(strCollection[0])
        g = float(strCollection[1])
        b = float(strCollection[2])
        color = c4d.Vector(r,g,b)
        obj[element] = color
    elif type == c4d.DTYPE_COLOR and "(a)" in name:
        strCollection = value.split("_")
        r = float(strCollection[3])
        g = float(strCollection[3])
        b = float(strCollection[3])
        color = c4d.Vector(r,g,b)
        obj[element] = color
    elif type == c4d.DTYPE_VECTOR and "(xyz)" in name:
        strCollection = value.split("_")
        r = float(strCollection[0])
        g = float(strCollection[1])
        b = float(strCollection[2])
        color = c4d.Vector(r,g,b)
        obj[element] = color
    elif type == c4d.DTYPE_REAL and "(w)" in name:
        strCollection = value.split("_")
        obj[element] = float(strCollection[3])
    else:
        obj[element] = value

    c4d.EventAdd()
    
    return element

def AnalyzeFieldInfo(sourceString):
    result = list()
    fields = sourceString.split("&")
    
    for item in fields:
        fieldInfo = item.split("@")
        name = Decode(fieldInfo[0].split(":")[1])
        type = fieldInfo[1].split(":")[1]
        value = Decode(fieldInfo[2].split(":")[1])
        
        result.append((name,type,value))

    return result

def Decode(string):
    return base64.b64decode(string)

def Encode(string):
    return base64.b64encode(string.encode("utf-8"))

class Entry(plugins.ShaderData):
    
    mainBitmap = None
    mUnityContent = ""
    
    
    def __init__(self):
        self.SetExceptionColor(c4d.Vector(1.0, 0.0, 0.0))

    def Init(self, node):
        
        node[C4D_DISPLAY_TEXTURE] = self.mainBitmap
        node[UNITY_CONTENT] = self.mUnityContent

        return True

    def InitRender(self, sh, irs):
        
        self.mainBitmap = sh[C4D_DISPLAY_TEXTURE]
        self.mUnityContent = sh[UNITY_CONTENT]
        
        if self.mainBitmap:
            self.mainBitmap.InitRender(irs)

        return 0

    def FreeRender(self, sh):
    
        if (self.mainBitmap):
            self.mainBitmap.FreeRender()
        
        self.mainBitmap = None

    def Message(self, node, type, data):

        if type==c4d.MSG_DESCRIPTION_COMMAND:
            bt = data['id'][0].id 
            doc = c4d.documents.GetActiveDocument()
            
            if bt == ANALYZE_BTN:
                
                cacheTexturesList = CacheTexturesPath(node)
                
                ClearUserData(node)
                
                content = node[UNITY_CONTENT]
                splitInfo = content.split("*")
                
                if len(splitInfo) < 2:return True
                
                projPath = os.path.join(doc.GetDocumentPath(), doc.GetDocumentName())
                
                group = AddUserData(node, c4d.DTYPE_GROUP, c4d.DTYPE_NONE, "BaseInfo", None, None)
                
                AddUserData(node, c4d.DTYPE_STATICTEXT, c4d.DTYPE_STRING, "ProjPath", group, projPath)
                AddUserData(node, c4d.DTYPE_STATICTEXT, c4d.DTYPE_STRING, "ShaderName", group, Decode(splitInfo[0]))
                
                listInfo = AnalyzeFieldInfo(splitInfo[1])
                
                for item in listInfo:
                    name,type,value = item

                    if type == "float":
                        AddUserData(node, c4d.DTYPE_REAL, c4d.DESC_UNIT_FLOAT, name, None, float(value))

                    if type == "vector2":
                        AddUserData(node, c4d.DTYPE_VECTOR, c4d.DESC_UNIT_FLOAT, name, None, value)

                    if type == "vector4":
                        AddUserData(node, c4d.DTYPE_VECTOR, c4d.DESC_UNIT_FLOAT, name+"(xyz)", None, value)
                        AddUserData(node, c4d.DTYPE_REAL, c4d.DESC_UNIT_FLOAT, name+"(w)", None, value)

                    if type == "color":
                        AddUserData(node, c4d.DTYPE_COLOR, c4d.DESC_UNIT_FLOAT, name+"(rgb)", None, value)
                        AddUserData(node, c4d.DTYPE_COLOR, c4d.DESC_UNIT_FLOAT, name+"(a)", None, value)

                    if type == "texture":
                        def predicate(m):return m[0] == name
                        target = FirstOrDefault(cacheTexturesList, predicate)
                        dstTex = ""
                        if not target == None: dstTex = target[1]

                        AddUserData(node, c4d.DTYPE_TEXTURE, None, name, None, dstTex)
                        
                    if type == "cubemap":
                    
                        names = [name+"(+x)", name+"(+y)", name+"(+z)", name+"(-x)", name+"(-y)", name+"(-z)"]
                        
                        for item in names:
                        
                            def predicate(m):return m[0] == item
                            target = FirstOrDefault(cacheTexturesList, predicate)
                            dstTex = ""
                            if not target == None: dstTex = target[1]
                        
                            AddUserData(node, c4d.DTYPE_TEXTURE, None, item, None, dstTex)
                
        return True
        
    def Output(self, sh, cd):
    
        col = c4d.Vector(0.0, 0.0, 0.0)
        
        if self.mainBitmap:
            col = self.mainBitmap.Sample(cd)
 
        return col

if __name__ == "__main__":
    plugins.RegisterShaderPlugin(PLUGIN_ID, "C2U ShaderConnecter", 0, Entry, "c2u_shader_connect", 1000)